package com.hoongs.v1.service.impl;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.hoongs.v1.dto.UserPageDto;
import com.hoongs.v1.entity.LoginUser;
import com.hoongs.v1.entity.User;
import com.hoongs.v1.entity.WxAuth;
import com.hoongs.v1.entity.WxUserInfo;
import com.hoongs.v1.mapper.UserMapper;
import com.hoongs.v1.service.UserService;
import com.hoongs.v1.service.WxService;
import com.hoongs.v1.utils.*;
import io.jsonwebtoken.Claims;
import lombok.SneakyThrows;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestBody;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Service
public class UserServiceImpl implements UserService {

    public static final String NOT_FOUND_ARTICLE = "没有找到该用户！";

    @Value("${wxmini.appid}")
    private String appid;

    @Value("${wxmini.secret}")
    private String secret;

    @Resource
    private UserMapper userMapper;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private WxService wxService;

    @SneakyThrows
    @Override
    public Result getSessionId(String code) {
        if (StringUtils.isBlank(code)) return Result.opError("code不能为空！");
        // 访问微信登录校验接口并获取返回结果
        String wxUrl = "https://api.weixin.qq.com/sns/jscode2session?appid={0}&secret={1}&js_code={2}&grant_type=authorization_code";
        String replaceUrl = wxUrl.replace("{0}", appid).replace("{1}", secret).replace("{2}", code);
        String res = HttpUtil.get(replaceUrl);
        com.alibaba.fastjson2.JSONObject jsonObject = JSON.parseObject(res);
        // 获取到返回结果后存储到redis
        if (StringUtils.isNotBlank((String) jsonObject.get("errcode"))) return Result.opError("code过期！");
        if (StringUtils.isBlank((String) jsonObject.get("openid")) || StringUtils.isBlank((String) jsonObject.get("session_key"))) {
            return Result.opError("操作失败！");
        } else {
            String uuid = JwtUtil.getUUID();
            redisUtil.setCacheObject("wx-session:" + uuid, res, 30, TimeUnit.MINUTES);
            Map<String, String> resMap = new HashMap<>();
            resMap.put("sessionId", uuid);
            return Result.opSuccess("认证成功！", resMap);
        }
    }

    @SneakyThrows
    @Override
    public Result authLogin(WxAuth wxAuth) {
        try {
            // 解密校验获得用户敏感信息
            String wxRes = wxService.wxDecrypt(wxAuth.getEncryptedData(), wxAuth.getSessionId(), wxAuth.getIv());
            WxUserInfo wxUserInfo = JSON.parseObject(wxRes, WxUserInfo.class);
            // 校验获取的信息中没有openid，手动从redis中取出存入
            String jsonStr = redisUtil.getCacheObject("wx-session:" + wxAuth.getSessionId());
            JSONObject jsonObject = JSON.parseObject(jsonStr);
            wxUserInfo.setOpenId((String) jsonObject.get("openid"));
            wxUserInfo.setUnionId((String) jsonObject.get("unionid"));

            // 根据获取的openid判断是否已有该用户，有登录，无注册
            User findUserInfo = userMapper.selectOne(new QueryWrapper<User>().eq("openid", wxUserInfo.getOpenId()));
            if (findUserInfo != null) {
                User user = new User();
                BeanUtils.copyProperties(findUserInfo, user);
                return Result.opSuccess("操作成功！", this.wxLogin(user));
            } else {
                User user = new User();
                user.from(wxUserInfo);
                return this.wxRegister(user);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return Result.opError();
    }

    /**
     * 小程序登录
     *
     * @param user
     * @return
     */
    public Map<String, Object> wxLogin(User user) {
        // 生成token
        String token = JwtUtil.createJWT(user.getId());
        List<String> roleList = new ArrayList<>();
        if ("9".equals(user.getRole())) {
            roleList.add("admin");
        }
        LoginUser loginUser = new LoginUser(user, roleList);
        redisUtil.setCacheObject("login:" + user.getId(), loginUser, 5, TimeUnit.HOURS);

        // 删除敏感信息，返回前端
        user.setPassword(null);
        user.setOpenid(null);
        user.setUnionid(null);

        Map<String, Object> map = new HashMap<>();
        map.put("token", token);
        map.put("user", user);
        return map;
    }

    public Result wxRegister(User userInfo) {
        User user = new User();
        BeanUtils.copyProperties(userInfo, user);
        User queryUser = userMapper.selectOne(new QueryWrapper<User>().eq("openid", userInfo.getOpenid()));
        if (queryUser == null) {
            user.setStatus("1");
            user.setSummary("小程序用户: " + user.getUsername());
            userMapper.insert(user);
        }
        return Result.opSuccess("操作成功！", this.wxLogin(user));
    }

    /**
     * 账号密码登录
     *
     * @param username
     * @param password
     * @param code
     * @return
     */
    @Override
    public Result login(@RequestBody String username, @RequestBody String password, @RequestBody String code) {
        LoginUser loginUser = this.getLoginUser(username, password);
        String userId = loginUser.getUser().getId();

        String jwt = JwtUtil.createJWT(userId);
        redisUtil.setCacheObject("login:" + userId, loginUser, 5, TimeUnit.HOURS);

        Map<String, Object> map = new HashMap<>();
        map.put("token", jwt);
        map.put("user", loginUser.getUser());

        return Result.opSuccess("登录成功", map);
    }

    /**
     * 账号密码退出
     *
     * @return
     */
    @Override
    public Result logout() {
        // 获取 SecurityContextHolder 中的用户信息
        UsernamePasswordAuthenticationToken authentication = (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        if (Objects.isNull(loginUser)) {
            throw new RuntimeException("您还未登录！");
        }
        String userId = loginUser.getUser().getId();
        // 删除redis中的值
        redisUtil.deleteObject("login:" + userId);
        return Result.opSuccess("退出登录成功");
    }

    @Override
    public Result getAuthStatus(String token) {
        User user = UserServiceImpl.getUserInfoByToken(token, userMapper);
        if (user == null) return Result.opError("未查询到用户信息！");
        return Result.success("获取数据成功", user.getIsAuth());
    }

    @Override
    public Result setAuthStatus(String isAuth, String token) {
        User user = UserServiceImpl.getUserInfoByToken(token, userMapper);
        user.setIsAuth(isAuth);
        return update(user);
    }

    @Override
    public Result save(User user) {
        if (StringUtils.isNotBlank(user.getId())) {
            return this.update(user);
        } else {
            return this.add(user);
        }
    }

    @Override
    public Result add(User user) {
        QueryWrapper<User> wrapper = new QueryWrapper();
        wrapper.eq("username", user.getUsername());
        List<User> list = userMapper.selectList(wrapper);
        if (list.size() > 0) {
            return Result.opError("该用户名已被占用");
        }
        if (userMapper.insert(user) == 1) {
            return Result.opSuccess();
        } else {
            return Result.opError();
        }
    }

    @Override
    public Result remove(String id) {
        if (!findUserById(id)) return Result.opError(NOT_FOUND_ARTICLE);
        if (userMapper.deleteById(id) == 1) {
            return Result.opSuccess();
        } else {
            return Result.opError();
        }
    }

    @Override
    public Result update(User user) {
        if (!findUserById(user.getId())) return Result.opError(NOT_FOUND_ARTICLE);
        if (userMapper.updateById(user) == 1) {
            return Result.opSuccess();
        } else {
            return Result.opError();
        }
    }

    @Override
    public Result selectById(String id) {
        User user = userMapper.selectById(id);
        if (user == null) return Result.error(NOT_FOUND_ARTICLE);
        return Result.success(user);
    }

    @Override
    public Result selectList(String name) {
        QueryWrapper<User> wrapper = new QueryWrapper<User>();
        wrapper.select("id", "username");
        if (StringUtils.isNotBlank(name)) {
            wrapper.like("username", name);
        }
        List<User> userList = userMapper.selectList(wrapper);
        return Result.success(userList);
    }

    @Override
    public Result selectPageList(UserPageDto userDto) {
        if (!PResult.checkPageQuery(userDto.getPage(), userDto.getSize())) return Result.error("分页参数错误");

        Page<User> daoPage = new Page<>(userDto.getPage(), userDto.getSize());
        QueryWrapper<User> wrapper = new QueryWrapper<User>();
        if (StringUtils.isNotBlank(userDto.getUsername())) {
            wrapper.like("username", userDto.getUsername());
        }
        if (StringUtils.isNotBlank(userDto.getMobile())) {
            wrapper.like("mobile", userDto.getMobile());
        }
        if (StringUtils.isNotBlank(userDto.getStatus())) {
            wrapper.eq("status", userDto.getStatus());
        }
        if (StringUtils.isNotBlank(userDto.getRole())) {
            wrapper.eq("role", userDto.getRole());
        }
        IPage<User> userDaoIPage = userMapper.selectPage(daoPage, wrapper);
        PResult rs = new PResult(userDaoIPage);
        return Result.success(rs);
    }

    /**
     * 根据Id查询用户是否存在
     */
    public Boolean findUserById(String id) {
        User user = userMapper.selectById(id);
        return user != null;
    }

    public LoginUser getLoginUser(String username, String password) {
        // authenticationManager实现用户认证
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
        Authentication authentication = authenticationManager.authenticate(authenticationToken);
        if (Objects.isNull(authentication)) {
            // 认证不通过时抛出异常
            throw new RuntimeException("登录失败");
        }
        // 认证通过后，用userid生成jwt存入result中并返回
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        return loginUser;
    }

    public static User getUserInfoByToken(String token, UserMapper mapper) {
        Claims claims = null;
        try {
            claims = JwtUtil.parseJWT(token);
        } catch (Exception e) {
            e.printStackTrace();
        }
        String userId = claims.getSubject();
        User user = mapper.selectById(userId);
        return user;
    }

}
